#include <vector>
#include <Logger/GlobalLogger.h>
#include <ObjectSubSystem/ObjectSubSystem.h>
#include <ObjectSubSystem/BaseObject.h>
#include <Math/MathConstants.h>
#include <Math/Bitfield.h>
#include <boost/pool/object_pool.hpp>
#include <Exceptions/Exception.h>

namespace cvObjects
{
/**
 * MEMORY allocation
 */
cvPool BaseObject::pool(sizeof(BaseObject));

BaseObject * BaseObject::create()	{ return new BaseObject(); }
inline void * BaseObject::operator new(size_t dummy)	{ return pool.malloc(); }

BaseObject::BaseObject()
: m_relativityflags(OBJ_NOTHING),
	m_validrelatives(0),
	m_dataptr(0),
	m_parent(0)
{}

BaseObject * BaseObject::copy(cvMath::bitfield_t flags)
{
	BaseObject * obj = ObjectSubSystem::getInstance()->create<BaseObject>();

	//obj->setParent(m_parent);		// clone parenting data?
	obj->m_relativityflags		= m_relativityflags;
	obj->m_dataptr		= m_dataptr;
	return obj;
}


inline void BaseObject::destroy()	{ delete this; }
BaseObject::~BaseObject()	{ detach(); }
void BaseObject::operator delete(void * ptr)	{ pool.free(ptr); }
void BaseObject::purgeMemoryPool()	{ pool.purge_memory(); }

/**
  *	TYPE OF the object*/
int BaseObject::objectType() const
{
	return GAMEOBJECT_BASE;
}

/**
  * Object parentting. */
void BaseObject::setParent(BaseObject * new_parent, cvMath::bitfield_t flags)
{
	if(m_parent)	{ LOGERROR("OBJECT::setParent(): object allredy has an parent!"); return; }

	if(new_parent)
	{
		/// check for circular parenting..
		BaseObject * tmp = new_parent;
		while(tmp)
		{
			if(tmp == tmp->m_parent)	{ LOGERROR("OBJECT::setParent(): circular refence detected, object refers to itself!"); }
			tmp = tmp->m_parent;
			if(tmp == this)	{ LOGERROR("OBJECT::setParent(): circular linkkage detected"); }
		}
		/// Set relativity.
		m_parent = new_parent;
		new_parent->m_child.push_back(this);
		setRelativity(flags);
		m_validrelatives = 0;
		if(m_relativityflags == OBJ_NOTHING)    { LOGWARN("OBJECT::setParent(): No relativity behaviour enabled."); }
	}
}

void BaseObject::setRelativity(cvMath::bitfield_t flags)
{
	if(m_relativityflags != flags)
	{
		cvMath::setFlag_on(m_relativityflags, cvMath::filterFlags(flags, OBJ_DATA));
	}
}

BaseObject * BaseObject::getParent() const	{ return m_parent; }

size_t BaseObject::countChildren() const
{
	return (m_child.empty()) ? 0 : m_child.size();
}

BaseObject * BaseObject::getChild(size_t index) const
{
	if(m_child.empty())
	{
		LOGERROR("OBJECT::getChild(): object has no children");
	}
	if(m_child.size() <= index)
	{
		LOGERROR("OBJECT::getChild(): invalid index");
		return 0;
	}

	return m_child[index];
}

void BaseObject::detach()
{
	BaseObject * p;
	while(!m_child.empty())
	{
		p = m_child.back();
		m_child.pop_back();
		p->update_linear();				// refresh object, so we don't miss any attribute changes.
		p->setRelativity(OBJ_NOTHING);	// reset object relativity mode.
		p->m_relativityflags = OBJ_NOTHING;
		p->m_parent = 0;
	}
	if(m_parent)
	{
		for(baseobj_vector_t::iterator it = m_parent->m_child.begin(); it != m_parent->m_child.end(); ++it)
		{
			if(*it == this)
			{
				m_parent->m_child.erase(it);
				break;
			}
		}
	}
	setRelativity(OBJ_NOTHING);
	m_parent = 0;
	m_relativityflags = OBJ_NOTHING;
	m_validrelatives = 0;
}


/**
  *	UPDATES the parent-children tree */
void BaseObject::update()
{
	if(m_parent)
	{	// we are in middle of the parent-children tree. Search object wich
		// relative attributes are uptodate and start updating there.
		BaseObject * broot = searchForRoot(m_relativityflags, 0);
		if(broot)	{ broot->update_tree(); }
	}
	else
	{
		for(baseobj_vector_t::iterator it = m_child.begin(); it != m_child.end(); ++it)
		{
			(*it)->update_tree();
		}
	}
}

BaseObject * BaseObject::searchForRoot(cvMath::bitfield_t flags, baseobj_vector_t * parents)
{
	BaseObject * pitr = m_parent;
	BaseObject * citr = this;
	BaseObject * retroot = 0;

	while((flags == OBJ_NOTHING || cvMath::someFlags_on(citr->m_relativityflags, flags)) && pitr)
	{
		if(cvMath::someFlags_off(citr->m_validrelatives, flags)
			&& cvMath::allFlags_on(pitr->m_validrelatives, flags))	{ retroot = citr; }

		if(parents) { parents->push_back(citr); }

		citr = citr->m_parent;
		pitr = pitr->m_parent;
	}
	return retroot;
}

void BaseObject::update_tree()
{	// pull attributes from parent:
	updater();
	// recruse into children:
	for(baseobj_vector_t::iterator it = m_child.begin(); it != m_child.end(); ++it)
	{
		(*it)->update_tree();
	}
}

void BaseObject::update_linear()
{
	static baseobj_vector_t s_tmpvec;

	s_tmpvec.clear();
	searchForRoot(m_relativityflags, &s_tmpvec);

	while(!s_tmpvec.empty())
	{
		s_tmpvec.back()->updater();
		s_tmpvec.pop_back();
	}
}

void BaseObject::updater()
{
	if(cvMath::allFlags_on(m_relativityflags, OBJ_DATA))
	{
		m_dataptr = m_parent->m_dataptr;		// update ptr
		cvMath::setFlag_on(m_validrelatives, OBJ_DATA);
	}
}



/**
  *	SETTERS */
void BaseObject::setData(void * ptr)
{
	m_dataptr = ptr;
	cvMath::setFlag_off(m_validrelatives, OBJ_DATA);	// invalidate setter spesific data..
}

/**
  *	GETTERS */
void * BaseObject::getData()
{
	update_linear();	// run getter spesific procedure..
	return m_dataptr;
}
};
